﻿using System;
using System.Collections.Generic;
using System.Linq.Expressions;


namespace Devtm.ServiceModel.Discovery.Collection
{

    /// <summary>
    /// ViewCollection
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <typeparam name="TKey">The type of the key.</typeparam>
    public class SubCollection<T2, TKey2, T, TKey> : TableCollection<T2, TKey2>
    {

        #region private
       
        internal Expression<Func<T, T2>> Filter;
        private Func<T, T2> _filter;
        private ITableCollection<T, TKey> list;

        #endregion


        #region ctor / ~

        internal SubCollection(
            TableCollection<T, TKey> list,
            Expression<Func<T, T2>> filter
            )
            : base()
        {

            this.list = list;
            Filter = filter;
            _filter = filter.Compile();

            list.CollectionChanged += list_CollectionChanged;
            list.PropertyItemChanged += list_PropertyItemChanged;

            foreach (T item in list)
            {
                T2 t2 = _filter(item);
                if (!this.Contains(t2))
                    base.Add(t2);

            }

        }

        public override void Dispose()
        {
            base.Dispose();
            list.CollectionChanged -= list_CollectionChanged;
            list.PropertyItemChanged -= list_PropertyItemChanged;
        }

        #endregion

        #region Events

        void list_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {

            e.ApplyChange<T2>
              (
                  item => base.Add(item),     // Add
                  item => base.Remove(item),  // remove
                  () => Clear()               // Clear
              );

            //if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Add)
            //    foreach (T item in e.NewItems)
            //    {
            //        T2 f = _filter(item);
            //        if (!ContainsKeyFrom(f))
            //            base.Add(f);
            //    }
            //else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove)
            //    foreach (T item2 in e.OldItems)
            //    {
            //        T2 f2 = _filter(item2);
            //        if (ContainsKeyFrom(f2))
            //            base.Remove(f2);
            //    }


            //else if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Reset)
            //    Clear();

        }

        void list_PropertyItemChanged(object sender, ItemPropertyChangedEventArgs<T> e)
        {
            T2 f = _filter(e.Item);
            bool c = ContainsKeyFrom(f);

            if (!c)
                base.Add(f);

            else if (c)
                base.Remove(f);
        }

        #endregion


        #region Methods

        /// <summary>
        /// Refresh the list if the filter have changed
        /// </summary>
        public void Refresh()
        {


            // on recupère tout les éléments qui sont dans le filtre mais aps encore la
            foreach (T item in list)
            {
                T2 t2 = _filter(item);
                if (!this.Contains(t2))
                    base.Add(t2);
            }


            // on supprime les elements qui sont dans la liste mais pas dans le filtre
            foreach (T item in list)
            {
                T2 t2 = _filter(item);
                if (!this.Contains(t2))
                    base.Remove(t2);
            }

        }


        /// <summary>
        /// Adds the specified item to the the liste and return the item
        /// </summary>
        /// <param name="item">The item.</param>
        public T Add(T item)
        {
            return list.Add(item);
        }


        /// <summary>
        /// Adds the range Item.
        /// </summary>
        /// <param name="items">The items.</param>
        public void AddRange(IList<T> items)
        {
            list.AddList(items);
        }


        /// <summary>
        /// Removes the specified item.
        /// </summary>
        /// <param name="item">The item.</param>
        public void Remove(T item)
        {
            list.Remove(item);
        }


        /// <summary>
        /// Removes the item specified by the key and return the dettached element.
        /// </summary>
        /// <param name="key">The key.</param>
        public void RemoveKey(TKey key)
        {
            list.RemoveKey(key);
        }


        /// <summary>
        /// Removes the item specified by the key and return the dettached element.
        /// </summary>
        /// <param name="key">The key.</param>
        public bool TryRemove(TKey key)
        {
            return list.TryRemoveKey(key);
        }


        /// <summary>
        /// Removes the item specified by the key and return the dettached element.
        /// </summary>
        /// <param name="key">The key.</param>
        public bool TryRemove(T item)
        {
            return TryRemove(item);
        }


        /// <summary>
        /// Remplace l'élément à l'index spécifié.
        /// </summary>
        /// <param name="item">
        /// Nouvelle valeur de l'élément à l'index spécifié dans l'item
        /// </param>
        public void Update(T item)
        {
            list.Update(item);
        }

        #endregion


    }
}
